/****************************************
* James Gomez                           *
* Project 3: Target Practice P3         *
* Comp 465                              *
* Professor Barnes                      *
* 12/7/2012                             *
*****************************************/

#ifndef _PLAYER_SHIP_
#define _PLAYER_SHIP_

#include "GomezJamesP3_OpenGL.h"

#include "GomezJamesP3_Movable.cpp"
#include "GomezJamesP3_CollidableObject.cpp"
#include "GomezJamesP3_Explodable.cpp"

#include "GomezJamesP3_ChaseCamera.cpp"
#include "GomezJamesP3_ShipTorpedo.cpp"
#include "GomezJamesP3_PlayerShip.cpp"

#include "GomezJamesP3_drawTriModel.cpp"

#include <string>
#include <iostream>
#include <math.h>
using namespace std;

extern ::vector<Torpedo*> torpedos;

/***********************************************************************
3D Movable Object class for the Player's Warbird. Users can rotate the 
ship about the x,y,z axes via the yaw, roll, and pitch methods as well 
as thrust forward and back via the thrustUp and thrustBack methods. 
************************************************************************/
class PlayerShip: public CollidableObject, Movable, public Explodable{

private:
	GLfloat startLoc[3];
	GLuint shipModelId;
	GLint torpedoCount;
	Torpedo * currentTorpedo;
	GLfloat missileLifeSpan;
	bool gravity;
	GLfloat GM[16]; //gravity matrix

public:
	PlayerShip(const string name, GLfloat startLoc[3]);
	~PlayerShip(){}
	
	void draw();
	void move();	

	inline void yawLeft(const GLfloat angle);
	inline void yawRight(const GLfloat angle);
	inline void rollLeft(const GLfloat angle);
	inline void rollRight(const GLfloat angle);
	inline void pitchUp(const GLfloat angle);
	inline void pitchDown(const GLfloat angle);
	inline void thrustUp(const GLfloat speed);
	inline void thrustBack(const GLfloat speed);

	bool isGravityOn(){ return gravity; }
	void setGravity(bool flag){ gravity  = flag; }

	void fireTorpedo();
	Torpedo * getCurrentTorpedo() const { return currentTorpedo; }
	void warp(vector<ChaseCamera *>::iterator warpIterator);

	void resetGM();
};


PlayerShip::PlayerShip(const string name, GLfloat start[3]): 
	CollidableObject(name, 120), Explodable(){
	
	for (int i=0; i<3; i++){
		startLoc[i] = start[i];
	}

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(this->TM);
	glTranslatef(startLoc[0], startLoc[1], startLoc[2]);
	glGetFloatv(GL_MODELVIEW_MATRIX, this->TM);
	glPopMatrix();

	shipModelId =  glGenLists(1);

	torpedoCount = 30;
	currentTorpedo = NULL;

	resetGM();
};

void PlayerShip::draw(){

	if(isActive){
		if(isExploding){
			drawExplosion(TM);
		}
		else{
			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();	
			glMultMatrixf(this->TM);	
			//drawAxes(500);	
			glEnable(GL_NORMALIZE);
			drawTriModel(shipModelId, 1.8f);
			//glCallList(shipModelId);  //Problem with dispay list. Using immediate mode for now.	
			glPopMatrix();
		}
	}

}

void PlayerShip::move(){

	if(isActive){
		if (isExploding){
			explode();
		}		
		else{
			glMatrixMode(GL_MODELVIEW);			

			if(gravity){
				glPushMatrix();
				GLfloat distanceFromSun = getLocationV().magnitude();
				GLfloat gravityForce = GRAVITY_CONST/(distanceFromSun*distanceFromSun);
				abs(gravityForce);				
				GLfloat frameChange = distanceFromSun/gravityForce;
				GLfloat x,y,z;
				x = getX()/frameChange;
				y = getY()/frameChange;
				z = getZ()/frameChange;
				glLoadIdentity();
				glTranslatef(-x, -y, -z);
				glGetFloatv(GL_MODELVIEW_MATRIX, GM);
				glPopMatrix();
			}

			glPushMatrix();
			glLoadMatrixf(TM);
			glMultMatrixf(OM);
			glMultMatrixf(GM);
			glGetFloatv(GL_MODELVIEW_MATRIX, TM);
			resetOM();
			resetGM();
			glPopMatrix();
		}		
		
		if (currentTorpedo != NULL){
			if (!currentTorpedo->active())		
				currentTorpedo = NULL;
		}
	}

}

inline void PlayerShip::yawLeft(const GLfloat angle){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glRotatef(angle, 0, 1, 0);
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

inline void PlayerShip::yawRight(const GLfloat angle){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glRotatef(angle, 0, -1, 0); 
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

inline void PlayerShip::rollLeft(const GLfloat angle){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glRotatef(angle, 0, 0, -1);
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

inline void PlayerShip::rollRight(const GLfloat angle){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glRotatef(angle, 0, 0, 1);
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

inline void PlayerShip::pitchUp(const GLfloat angle){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glRotatef(angle, -1, 0, 0);
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

inline void PlayerShip::pitchDown(const GLfloat angle){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glRotatef(angle, 1, 0, 0);
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

inline void PlayerShip::thrustUp(const GLfloat speed){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glTranslatef(0, 0, speed);
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

inline void PlayerShip::thrustBack(const GLfloat speed){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(OM);
	glTranslatef(0, 0, -speed);
	glGetFloatv(GL_MODELVIEW_MATRIX, OM);
	glPopMatrix();
}

void PlayerShip::fireTorpedo(){
	
	if (torpedoCount <= 0)
		cout<< getName() << " out of Torpedos. YOU LOSE!" <<endl;
	else{
		if(isActive && currentTorpedo == NULL){
			cout<< "Fire" <<endl;
			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			glLoadMatrixf(TM);
			glTranslatef(0, 0, 200);
			GLfloat m[16];
			glGetFloatv(GL_MODELVIEW_MATRIX, m);
			glPopMatrix();

			currentTorpedo = new ShipTorpedo(260, m);
			torpedos.push_back(currentTorpedo);			
			torpedoCount -= 1;
		}		
	}
	
}

void PlayerShip::warp(vector<ChaseCamera *>::iterator warpIterator){
	cout<< "Warp to " << (*warpIterator)->getName() << endl;
	GLfloat m[16];
	(*warpIterator)->getTM(m);
	this->setTM(m);
}

void PlayerShip::resetGM(){
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glGetFloatv(GL_MODELVIEW_MATRIX, GM);
	glPopMatrix();
}


#endif